package cn.whale.killorder.mq.handler;

import cn.whale.basic.constants.MQConstants;
import cn.whale.basic.constants.RedisConstants;
import cn.whale.basic.utils.AssertUtil;
import cn.whale.kill.to.CourseKillTo;
import cn.whale.killorder.domain.KillOrder;
import cn.whale.killorder.service.IKillOrderService;
import cn.whale.pay.dto.AlipayNotifyDto;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.mapper.Wrapper;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import static cn.whale.basic.constants.RedisConstants.*;

//订单消费者

@Slf4j

@Component
public class KillOrderHandler {

    @Autowired
    private IKillOrderService killOrderService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private RedisTemplate<String,Object> redisTemplate;



    //支付结果处理
    @RabbitListener(queues = MQConstants.QUEUE_PAY_NOTEFIY,containerFactory = "rabbitListenerContainerFactory")
    public void orderPayNotifyHandler(AlipayNotifyDto alipayNotifyDto, Message message , Channel channel) throws IOException {
         KillOrder killOrder = killOrderService.selectByOrderNo(alipayNotifyDto.getOut_trade_no());
         if(killOrder != null){
             //验证签名
             if(killOrder.getTotalPrice().compareTo(new BigDecimal(alipayNotifyDto.getTotal_amount())) == 0
             && killOrder.getOrderNo().equals(alipayNotifyDto.getOut_trade_no()) ){
                 //处理订单状态
                 if(alipayNotifyDto.isTradeSuccess()){
                     killOrder.setStatusPay(KillOrder.STATUS_PAY_OK);
                     killOrder.setStatusOrder(KillOrder.STATUS_ORDER_UN_DELIVERY);
                 }else{
                     killOrder.setStatusPay(KillOrder.STATUS_PAY_FAILURE);
                     killOrder.setStatusOrder(KillOrder.STATUS_ORDER_ERROR);
                 }
                 //修改订单状态
                 killOrder.setPayUpdateTime(new Date());
                 killOrderService.updateById(killOrder);

                 //这里不设计到库存操作，毕竟卖的不是商品，而且我们在秒杀的业务中预减了库存
                 ack(message, channel);
             }
         }
    }


        //下单业务
    @RabbitListener(queues = MQConstants.QUEUE_ORDER_KILL,containerFactory = "rabbitListenerContainerFactory")
    public void orderHandler(CourseKillTo to, Message message , Channel channel) throws IOException {
        log.info("订单消费者处理订单{}",to);
        insertOrder(to, KillOrder.STATUS_ORDER_UN_PAY,KillOrder.STATUS_PAY_UN_PAY );

        //延迟队列处理支付失败，当然秒杀场景也可不做这个
        log.info("下单成功，加入订单支付过期延迟队列{}",to);

        rabbitTemplate.convertAndSend(MQConstants.EXCHNAGE_ORDER_KILL,MQConstants.ROUTINGKEY_QUEUE_KILL_DELAY,to);
        //往Redis存放一个下单结果 ， 如果不放的话，支付的时候可以去数据库查询
        BoundHashOperations<String, Object, Object> boundHashOperations = redisTemplate.boundHashOps(KEY_KILL_USER_RESULT);
        boundHashOperations.put(to.getUserId(),to);

        //这里不设计到库存操作，毕竟卖的不是商品，而且我们在秒杀的业务中预减了库存
        ack(message, channel);
    }

    //过期的订单
   // @RabbitListener(queues = MQConstants.QUEUE_ORDER_EXPIRED)
    public void orderExpireHandler(CourseKillTo to, Message message , Channel channel) throws IOException {
        log.info("订单过期处理{}",to);
        //订单过期，保存一个记录
        Wrapper<KillOrder> wrapper = new EntityWrapper<KillOrder>().eq("order_no", to.getOrderNo());
        KillOrder killOrder = killOrderService.selectOne(wrapper);
        if(killOrder != null && killOrder.getStatusOrder().intValue() == KillOrder.STATUS_ORDER_UN_PAY && killOrder.getStatusPay() == KillOrder.STATUS_PAY_UN_PAY){
            killOrder.setStatusOrder(KillOrder.STATUS_ORDER_ERROR);
            killOrder.setStatusPay(KillOrder.STATUS_PAY_FAILURE);
            killOrderService.updateById(killOrder);
            //退库存，这里不设计到Mysql，直接把Redis信号量加回去即可
            RSemaphore semaphore = redissonClient.getSemaphore(String.format(KEY_KILL_COURSE_SEMAPHORE, to.getCourseId()));
            semaphore.addPermits(to.getKillCount());
            log.info("订单转改修改为失败，回退库存{}",semaphore.availablePermits());

            //删除用户的秒杀记录，可以再次秒杀
            String userKey = String.format(KEY_KILL_USER_LOG, to.getUserId().toString());
            redisTemplate.delete(userKey);
        }
        ack(message, channel);
    }


    private void insertOrder(CourseKillTo to,Integer statusOrder,Integer statusPay){
        Wrapper<KillOrder> wrapper = new EntityWrapper<KillOrder>().eq("order_no", to.getOrderNo());
        KillOrder killOrder = killOrderService.selectOne(wrapper);
        if(killOrder == null){
            killOrder = new KillOrder();
            killOrder.setCreateTime(new Date());
            killOrder.setKillCount(to.getKillCount());
            killOrder.setOrderNo(to.getOrderNo());
            //待支付
            killOrder.setStatusOrder(statusOrder);
            killOrder.setStatusPay(statusPay);
            killOrder.setTotalPrice(to.getTotalPrice());
            String[] ids = to.getCourseId().split("_");
            killOrder.setCourseId(Long.valueOf(ids[1]));
            killOrder.setSessionsId(Long.valueOf(ids[0]));
            killOrderService.insert(killOrder);
        }
    }

    private void ack(Message message, Channel channel) throws IOException {
        try {
            channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
        } catch (Exception e) {
            e.printStackTrace();
            channel.basicReject(message.getMessageProperties().getDeliveryTag(), true);
        }
    }
}
